//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_REGISTER_H__
#define __ELASTOS_REGISTER_H__

//
// EFLAG
//
#define EFLAG_CF        __32BIT(0)          // Carry Flag            (status)
#define EFLAG_PF        __32BIT(2)          // Parity Flag           (status)
#define EFLAG_AF        __32BIT(4)          // Auxiliary Carry Flag  (status)
#define EFLAG_ZF        __32BIT(6)          // Zero Flag             (status)
#define EFLAG_SF        __32BIT(7)          // Sign Flag             (status)
#define EFLAG_TF        __32BIT(8)          // Trap Flag             (SYSTEM)
#define EFLAG_IF        __32BIT(9)          // Interrupt Enable Flag (SYSTEM)
#define EFLAG_DF        __32BIT(10)         // Direction Flag        (control)
#define EFLAG_OF        __32BIT(11)         // Overflow Flag         (status)
#define EFLAG_IOPL(n)   ((u32_t)(n) << 12)  // I/O Privilege Level   (SYSTEM)
#define EFLAG_IOPL0     EFLAG_IOPL(0)
#define EFLAG_IOPL1     EFLAG_IOPL(1)
#define EFLAG_IOPL2     EFLAG_IOPL(2)
#define EFLAG_IOPL3     EFLAG_IOPL(3)
#define EFLAG_NT        __32BIT(14)         // Nested Task           (SYSTEM)
#define EFLAG_RF        __32BIT(16)         // Resume Flag           (SYSTEM)
#define EFLAG_VM        __32BIT(17)         // Virtual-8086 Mode     (SYSTEM)
#define EFLAG_AC        __32BIT(18)         // Alignment Check       (SYSTEM)
#define EFLAG_VIF       __32BIT(19)         // Virtual Interrupt Flag(SYSTEM)
#define EFLAG_VIP       __32BIT(20)         // Virtual Interrupt Pending(SYSTEM)
#define EFLAG_ID        __32BIT(21)         // ID Flag                (SYSTEM)

//
// CR0
//
// Protection Enable. Enables protected mode when set; enables realaddress
// mode when clear. This flag does not enable paging directly. It only enables
// segment-level protection. To enable paging, both the PE and PG flags must be
// set.
#define CR0_PE              __32BIT(0)

// Monitor Coprocessor. Controls the interaction of the WAIT (or
// FWAIT) instruction with the TS flag (bit 3 of CR0). If the MP flag is set,
// a WAIT instruction generates a device-not-available exception (#NM)
// if the TS flag is set. If the MP flag is clear, the WAIT instruction ignores
// the setting of the TS flag.
#define CR0_MP              __32BIT(1)

// Emulation. Indicates that the processor does not have an internal or
// external FPU when set; indicates an FPU is present when clear. When the EM
// flag is set, execution of a floating-point instruction generates a
// device-not-available exception(#NM). This flag must be set when the processor
// does not have an internal FPU or is not connected to a math coprocessor.
// If the processor does have an internal FPU, setting this flag would force
// all floating-point instructions to be handled by software emulation.
#define CR0_EM              __32BIT(2)

// Task Switched. Allows the saving of FPU context on a task switch to
// be delayed until the FPU is actually accessed by the new task. The processor
// sets this flag on every task switch and tests it when interpreting
// floating-point arithmetic instructions.
//  * If the TS flag is set, a device-not-available exception (#NM) is raised
//      prior to the execution of a floating-point instruction.
//  * If the TS flag and the MP flag (also in the CR0 register) are both set,
//      an #NM exception is raised prior to the execution of floating-point
//      instruction or a WAIT/FWAIT instruction.
// The processor does not automatically save the context of the FPU on a
// task switch. Instead it sets the TS flag, which causes the processor to
// raise an #NM exception whenever it encounters a floating-point instruction
// in the instruction stream for the new task. The fault handler for the #NM
// exception can then be used to clear the TS flag (with the CLTS instruction)
// and save the context of the FPU. If the task never encounters a
// floating-point instruction, the FPU context is never saved.
#define CR0_TS              __32BIT(3)

// Extension Type. Reserved in the P6 family and Pentium processors.
// (In the P6 family processors, this flag is hardcoded to 1.)
// In the Intel386 and Intel486 processors, this flag indicates
// support of Intel 387 DX math coprocessor instructions when set.
#define CR0_ET              __32BIT(4)

// Numeric Error. Enables the native (internal) mechanism for reporting
// FPU errors when set; enables the PC-style FPU error reporting mechanism
// when clear. When the NE flag is clear and the IGNNE# input is asserted,
// FPU errors are ignored. When the NE flag is clear and the IGNNE# input
// is deasserted, an unmasked FPU error causes the processor to assert
// the FERR# pin to generate an external interrupt and to stop instruction
// execution immediately before executing the next waiting floatingpoint
// instruction or WAIT/FWAIT instruction. The FERR# pin is intended to
// drive an input to an external interrupt controller (the FERR# pin emulates
// the ERROR# pin of the Intel 287 and Intel 387 DX math coprocessors).
// The NE flag, IGNNE# pin, and FERR# pin are used with external logic to
// implement PC-style error reporting.
#define CR0_NE              __32BIT(5)

// Write Protect. Inhibits supervisor-level procedures from writing into
// user-level read-only pages when set; allows supervisor-level procedures
// to write into user-level read-only pages when clear. This flag facilitates
// implementation of the copyon-write method of creating a new process
// (forking) used by operating systems such as UNIX*.
#define CR0_WP              __32BIT(16)

// Alignment Mask. Enables automatic alignment checking when set;
// disables alignment checking when clear. Alignment checking is performed
// only when the AM flag is set, the AC flag in the EFLAGS register is set,
// the CPL is 3, and the processor is operating in either protected or
// virtual-8086 mode.
#define CR0_AM              __32BIT(18)

// Not Write-through. When the NW and CD flags are clear,
// write-back (for Pentium and P6 family processors) or write-through
// (for Intel486?processors) is enabled for writes that hit the cache and
//  invalidation cycles are enabled.
#define CR0_NW              __32BIT(29)

// Cache Disable. When the CD and NW flags are clear, caching
// of memory locations for the whole of physical memory in the processor's
// internal (and external) caches is enabled. When the CD flag is set,
// caching is restricted as described in Table 9-4, in Chapter 9, Memory Cache
// Control. To prevent the processor from accessing and updating its caches,
// the CD flag must be set and the caches must be invalidated so that no cache
// hits can occur.
#define CR0_CD              __32BIT(30)

// Paging. Enables paging when set; disables paging when clear. When
// paging is disabled, all linear addresses are treated as physical addresses.
// The PG flag has no effect if the PE flag (bit 0 of register CR0) is not
// also set; in fact, setting the PG flag when the PE flag is clear causes
// a general-protection exception (#GP) to be generated.
#define CR0_PG              __32BIT(31)

//
// CR3
//
// Page-level Writes Transparent. Controls the write-through or writeback
// caching policy of the current page directory. When the PWT flag is set,
// writethrough caching is enabled; when the flag is clear, write-back caching
// is enabled. This flag affects only the internal caches (both L1 and L2,
// when present). The processor ignores this flag if paging is not used
// (the PG flag in register CR0 is clear) or the CD (cache disable) flag in CR0
// is set.
#define CR3_PWT             __32BIT(3)

// Page-level Cache Disable. Controls caching of the current page directory.
// When the PCD flag is set, caching of the page-directory is prevented;
// when the flag is clear, the page-directory can be cached. This flag affects
// only the processor's internal caches (both L1 and L2, when present).
// The processor ignores this flag if paging is not used (the PG flag in
// register CR0 is clear) or the CD (cache disable) flag in CR0 is set.
#define CR3_PCD             __32BIT(4)

//
// CR4
//
// Virtual-8086 Mode Extensions. Enables interrupt- and  exceptionhandling
// extensions in virtual-8086 mode when set; disables the extensions when clear.
// Use of the virtual mode extensions can improve the performance of
// virtual-8086 applications by eliminating the overhead of calling the
// virtual-8086 monitor to handle interrupts and exceptions that occur while
// executing an 8086 program and, instead, redirecting the interrupts and
// exceptions back to the 8086 program's handlers. It also provides hardware
// support for a virtual interrupt flag (VIF) to improve reliability of
// running 8086 programs in multitasking and multiple-processor environments.
#define CR4_VME             __32BIT(0)

// Protected-Mode Virtual Interrupts. Enables hardware support for a
// virtual interrupt flag (VIF) in protected mode when set; disables
// the VIF flag in protected mode when clear.
#define CR4_PVI             __32BIT(1)

// Time Stamp Disable. Restricts the execution of the RDTSC instruction
// to procedures running at privilege level 0 when set; allows RDTSC
// instruction to be executed at any privilege level when clear.
#define CR4_TSD             __32BIT(2)

// Debugging Extensions. References to debug registers DR4 and DR5
// cause an undefined opcode (#UD) exception to be generated when set;
// when clear, processor aliases references to registers DR4 and DR5 for
// compatibility with software written to run on earlier Intel Architecture
// processors.
#define CR4_DE              __32BIT(3)

// Page Size Extensions. Enables 4-MByte pages when set; restricts pages
// to 4 KBytes when clear.
#define CR4_PSE             __32BIT(4)

// Physical Address Extension. Enables paging mechanism to reference 36-bit
// physical addresses when set; restricts physical addresses to 32 bits when
// clear.
#define CR4_PAE             __32BIT(5)

// Machine-Check Enable. Enables the machine-check exception when set;
// disables the machine-check exception when clear.
#define CR4_MCE             __32BIT(6)

// Page Global Enable. (Introduced in the P6 family processors.) Enables
// the global page feature when set; disables the global page feature when
// clear. The global page feature allows frequently used or shared pages to
// be marked as global to all users (done with the global flag, bit 8, in a
// page-directory or page-table entry). Global pages are not flushed from the
// translation-lookaside buffer (TLB) on a task switch or a write to register
// CR3. In addition, the bit must not be enabled before paging is enabled via
// CR0.PG. Program correctness may be affected by reversing this sequence, and
// processor performance will be impacted.
#define CR4_PGE             __32BIT(7)

// Performance-Monitoring Counter Enable. Enables execution of the RDPMC
// instruction for programs or procedures running at any protection level when
// set; RDPMC instruction can be executed only at protection level 0 when clear.
#define CR4_PCE             __32BIT(8)

// Operating Sytsem FXSAVE/FXRSTOR Support (bit 9 of CR4). The operating
// system will set this bit if both the CPU and the OS support the use of
// FXSAVE/FXRSTOR for use during context switches.
#define CR4_OSFXSR          __32BIT(9)

// Operating System Unmasked Exception Support. The operating system will set
// this bit if it provides support for unmasked SIMD floating-point exceptions.
#define CR4_OSXMMEXCPT      __32BIT(10)

//
// DR6
//
#define DR6_B(n)            __32BIT(n)
#define DR6_BD              __32BIT(13)
#define DR6_BS              __32BIT(14)
#define DR6_BT              __32BIT(15)

//
// DR7
//
#define DR7_L(n)            __32BIT((n) * 2)
#define DR7_G(n)            __32BIT((n) * 2 + 1)
#define DR7_LE              __32BIT(8)
#define DR7_GE              __32BIT(9)
#define DR7_GD              __32BIT(13)

#define DR7_RWFIELD_EO      0x0
#define DR7_RWFIELD_WO      0x1
#define DR7_RWFIELD_IORW    0x2
#define DR7_RWFIELD_RW      0x3

#define DR7_RW(n, field)    ((field) << (16 + (n) * 4))

#define DR7_LENFIELD_1BYTE  0x0
#define DR7_LENFIELD_2BYTES 0x1
#define DR7_LENFIELD_4BYTES 0x3

#define DR7_LEN(n, field)   ((field) << (18 + (n) * 4))

//
// inline functions
//
INLINE void Cli()
{
    ASM("cli"
        : : :"memory");
}

INLINE void Sti()
{
    ASM("sti"
        : : :"memory");
}

INLINE uint32_t SaveFlags()
{
    uint32_t uFlags;
    ASM("pushfl;"
        "popl   %0;"
        :"=m"(uFlags));
    return uFlags;
}

INLINE uint32_t SaveFlagsAndCli()
{
    uint32_t uFlags;
    ASM("pushfl;"
        "popl   %0;"
        "cli;"
        :"=m"(uFlags));

    TIMEPEG_DISABLE_INTERRUPT

    return uFlags;
}

INLINE void RestoreFlags(uint_t uFlags)
{
    ASM("pushl  %0;"
        "popfl"
        : :"g"(uFlags));
}

INLINE void RestoreIF(uint_t uFlags)
{
    TIMEPEG_RESTORE_INTERRUPT

    if (uFlags & EFLAG_IF) ASM("sti;");
}

INLINE void SetFlags(uint_t uBitMask)
{
    ASM("pushfl;"
        "movl   (%%esp), %%eax;"
        "or     %%ecx, %%eax;"
        "movl   %%eax, (%%esp);"
        "popfl;"
        : :"c"(uBitMask) :"eax");
}

INLINE void ClearFlags(uint_t uBitMask)
{
    ASM("pushfl;"
        "movl   (%%esp), %%eax;"
        "and    %%ecx, %%eax;"
        "movl   %%eax, (%%esp);"
        "popfl;"
        : :"c"(~uBitMask) :"eax");
}

INLINE void Stts()
{
    ASM("movl   %%cr0, %%eax;"
        "orl    %0, %%eax;"
        "movl   %%eax, %%cr0;"
        : :"i"(CR0_TS) :"eax");
}

INLINE void Clts()
{
    ASM("clts;");
}

INLINE void Stne()
{
    ASM("movl   %%cr0, %%eax;"
        "orl    %0, %%eax;"
        "movl   %%eax, %%cr0;"
        : :"i"(CR0_NE) :"eax");
}

INLINE virtaddr_t GetPageFaultAddress()
{
    virtaddr_t cr2;
    ASM("movl   %%cr2, %%eax;"
        :"=a"(cr2));
    return cr2;
}

INLINE void SetPageFaultAddress(virtaddr_t cr2)
{
    ASM("movl   %%eax, %%cr2;"
        : :"a"(cr2));
}

INLINE void SetPageDirAddress(physaddr_t paBase)
{
    ASM("movl   %%eax, %%cr3;"
        : :"a"(paBase));
}

INLINE void EnablePaging(bool_t bEnable4MPages)
{
    if (bEnable4MPages) {
        ASM("movl   %%cr4, %%eax;"
            "orl    %0, %%eax;"
            "movl   %%eax, %%cr4;"
            : :"i"(CR4_PSE) :"eax");
    }

    ASM("movl    %%cr0, %%eax;"
        "orl     %0, %%eax;"
        "movl    %%eax, %%cr0;"
        : :"i"(CR0_PG) :"eax");
}

#define DEFINE_SET_DR(no) \
INLINE void SetDR##no(uint32_t value) \
{ \
    ASM("movl   %%eax, %%db"#no \
        : :"a"(value)); \
}

DEFINE_SET_DR(0)
DEFINE_SET_DR(1)
DEFINE_SET_DR(2)
DEFINE_SET_DR(3)
DEFINE_SET_DR(6)
DEFINE_SET_DR(7)

#define DEFINE_GET_DR(no) \
INLINE uint32_t GetDR##no() \
{ \
    uint32_t dr; \
    ASM("movl   %%db"#no", %%eax;" \
        :"=a"(dr)); \
    return dr; \
}

DEFINE_GET_DR(0)
DEFINE_GET_DR(1)
DEFINE_GET_DR(2)
DEFINE_GET_DR(3)
DEFINE_GET_DR(6)
DEFINE_GET_DR(7)

#endif //__ELASTOS_REGISTER_H__

